package event

import (
	//"errors"
	//"fmt"
	//"iot-base/common/cache"
	"iot-base/common/config"
	"iot-base/common/dbconn"
	"iot-base/common/event/handler"
	"iot-base/common/eventtype"
	//"iot-base/common/logger"
	//"iot-base/common/notify"
	"iot-base/common/property"
	//rc "iot-base/common/redis"
	"time"
	//"github.com/vmihailenco/msgpack/v5"
	//"encoding/json"
	//"gorm.io/gorm"
)

func init() {
	dbconn.DBConnection.AutoMigrate(&eventtype.AlarmState{})
	dbconn.DBConnection.AutoMigrate(&eventtype.AlarmHistroy{})
	//if config.PType != "cloud" {
	//创建分区表，对云端和边缘侧都建立为分区表
	property.AlarmHistroySplit(time.Now())
	//}
	//go delayClearAlarm()
}

type LAlarm *eventtype.Alarm

var alarmChan chan *eventtype.Alarm

func RemoveActiveAlarm(tid string) {
	var alarmInDB eventtype.AlarmState
	dbconn.DBConnection.Unscoped().Where("t_id=?", tid).Delete(&alarmInDB)
}

//SetAlarmChan 设置chan
func SetAlarmChan(alarmChanSet chan *eventtype.Alarm) {
	alarmChan = alarmChanSet
}

/*
func getAlarmDigit(id string, now int64, key *eventtype.AlarmKey) (*eventtype.AlarmDigist, bool, int64) {
	var alarmInRs eventtype.AlarmDigist
	//var alarmActive *ActiveAlarm
	isin, err, _ := rc.GetHashObj(id, &alarmInRs)
	isNeedRefresh := false
	if !isin || err != nil {
		isNeedRefresh = true
		//fmt.Println("****************************gethashobj*********", err, isin)
	} else { //告警在redis中存在
		isNeedRefresh = (now - alarmInRs.TimeRefreshed) >= 60*10
	}
	if isNeedRefresh {
		var alarmInDB eventtype.AlarmState
		errdb := dbconn.DBConnection.Model(&alarmInDB).Where("t_id=? and mindex=? and alarm_id=?",
			key.TID, key.Mindex, key.AlarmID).First(&alarmInDB).Error
		if errdb != nil {
			//alarmInDB.TimeRefreshed = now
			alarmInDB.IsAlarm = false
			alarmInDB.TimeChanged = eventtype.Datetime(time.Time{})
		}
		alarm := eventtype.AlarmDigist{
			//AlarmChanged:     alarmInDB.AlarmChanged,
			IsAlarm:       alarmInDB.IsAlarm,
			TimeChanged:   time.Time(alarmInDB.TimeChanged).Unix(),
			TimeRefreshed: now,
			Delay:         alarmInDB.Delay,
			Level:         alarmInDB.Level,
		}
		rc.SetHashObj(id, &alarm)

		return &alarm, isNeedRefresh, alarmInDB.UID
	}
	return &alarmInRs, isNeedRefresh, 0
}
*/
//ReportAlarm 上报告警
func ReportAlarm(tid string, aid string, index int64, flag string) {
	var curAlarm eventtype.Alarm
	curAlarm.TID = tid
	curAlarm.AlarmID = aid
	curAlarm.Mindex = index

	curAlarm.Flag = flag
	if flag == "BEGIN" {
		curAlarm.Time = time.Now().Unix()
		ThingAlarm(&curAlarm)
	} else {
		curAlarm.ClearTime = time.Now().Unix()
		ThingAlarmClear(&curAlarm)
	}
	if alarmChan != nil {
		if !(curAlarm.AlarmID == "offline" && curAlarm.TID == config.SelfOption.ID) {
			alarmChan <- &curAlarm
		}
	}

}

//ThingAlarm 告警 告警来临看是否有延时，有延时将redis中的消除干掉。
//redis缓冲key alarm_clear_set thing_id.XXXXX.alarm_id.index 分数为now+延时（到期时间）
//redis还会缓冲告警消除的内容 clear_cache_+id cleartime,(告警打包)
func ThingAlarm(curAlarm *eventtype.Alarm) {
	handler.HandleAlarm(curAlarm)
	/*
		isSelf := false
		if curAlarm.TID == config.SelfOption.ID {
			isSelf = true
		}
		isOfflineAlarm := curAlarm.AlarmID == "offline"
		pThing, err := cache.GetThing(curAlarm.TID)

		if !isSelf && err != nil || pThing == nil {
			return
		}

		tnow := time.Now()
		now := tnow.Unix()
		if curAlarm.Time == 0 {
			curAlarm.Time = now
		}

		id := cache.GetActiveAlarmID(curAlarm)

		pAlarmInRedis, _, seq := getAlarmDigit(id, now, &curAlarm.AlarmBase.AlarmKey)
		/*
			if isRefresh {
				cache.SaveAlarmToTid(curAlarm)
			}*/
	/*
		if isOfflineAlarm {
			curAlarm.AlarmName = "断线"
			//isOfflineAlarm = true
		}
		if pAlarmInRedis.TimeChanged >= curAlarm.Time { //告警为旧时告警，直接放弃
			if pAlarmInRedis.IsAlarm {
				cache.SaveAlarmToTid(curAlarm)
			} else {
				cache.RemoveAlarm(curAlarm)
			}
			return
		}
		var ckey = "cache_clear_" + id
		clearTime := rc.GetHashField(ckey, "clearTime")
		if clearTime == nil || clearTime.Err() != nil {
			ct, err := clearTime.Int64()
			if err == nil {
				if curAlarm.Time >= ct {
					rc.RemoveKey(ckey)
					rc.RemoveKeyFromSet("cache_clear", ckey)
				}
			}
		}

		if pAlarmInRedis.IsAlarm && pAlarmInRedis.TimeChanged != 0 {
			//在redis中已经有这个告警，此时告警不存在
			cache.SaveAlarmToTid(curAlarm)
			return
		}

		if pAlarmInRedis.TimeChanged == 0 || pAlarmInRedis.TimeChanged < curAlarm.Time { //redis中无记录或消除时间比较早

			_, pInfo := cache.GetEventInfo(pThing.Thing.ModelID, curAlarm.AlarmID)
			if !isSelf && pInfo == nil && curAlarm.AlarmID != "offline" {
				logger.SLog.Error("alarm is give up ---for model not contains alarmid", "alarm", curAlarm)
				return
			}

			if pThing != nil {
				curAlarm.ThingName = pThing.Thing.SName
				curAlarm.StationID = pThing.StationID
				station, _ := cache.GetThing(pThing.StationID)
				if station != nil {
					curAlarm.StationName = station.Thing.SName
				}
				model, _ := cache.GetModelOption(pThing.Thing.ModelID)
				if model != nil {
					curAlarm.CatalogID = model.CatalogID
				}
			}

			var delay int64 = 0
			//
			if curAlarm.Level == 0 {
				curAlarm.Level = pThing.Thing.Level
			}
			if isSelf {
				curAlarm.AlarmName = translateSelfAlarm(curAlarm.AlarmID)
			} else if pInfo != nil {
				delay = pInfo.Delay
				curAlarm.Level = pInfo.Level

				if curAlarm.AlarmName == "" {
					curAlarm.AlarmName = pInfo.Name
				}
			}

			if curAlarm.Info.Params == nil {
				curAlarm.Info.Params = make(map[string]interface{})
			}

			r, ok := curAlarm.Info.Params["reason"]
			isEmpty := false
			isEmpty = !ok
			isEmpty = isEmpty || r == nil
			if !isEmpty {
				text, ok := r.(string)
				if !ok || len(text) == 0 {
					isEmpty = true
				}
			}

			if isEmpty {
				curAlarm.Info.Params["reason"] = curAlarm.AlarmName
			}

			jobj, _ := json.Marshal(&curAlarm)

			var alarmDB = eventtype.AlarmState{
				UID: seq,
				//AlarmChanged.IsAlarm:true,
				//AlarmChanged.TimeChanged:curAlarm.Time,
				//AlarmChanged:AlarmChanged{IsAlarm:true,TimeChanged:curAlarm.Time},
				AlarmKey:    curAlarm.AlarmKey,
				AlarmObj:    jobj,
				Delay:       delay,
				StationID:   curAlarm.StationID,
				StationName: curAlarm.StationName,
				CatalogID:   curAlarm.CatalogID,
			}
			alarmDB.AlarmDisp = curAlarm.AlarmDisp
			alarmDB.TimeChanged = eventtype.Datetime(time.Unix(curAlarm.Time, 0))
			//fmt.Println("-------------alarmdb--------", alarmDB.TimeChanged)
			alarmDB.IsAlarm = true
			//告警存储到数据库中
			errdb := dbconn.DBConnection.Transaction(func(tx *gorm.DB) error {
				var dberr error
				pAlarmDB := &alarmDB

				if seq != 0 {
					dberr = tx.Save(pAlarmDB).Error
				} else {

					var alarmState eventtype.AlarmState
					err := tx.Model(pAlarmDB).Where("t_id=? and mindex=? and alarm_id=?",
						curAlarm.TID, curAlarm.Mindex, curAlarm.AlarmID).First(&alarmState).Error
					if alarmState.IsAlarm && err == nil {
						if pAlarmInRedis.TimeChanged == 0 { //redis中没有记录
							binfo, err := msgpack.Marshal(curAlarm.Info)
							var jinfo = ""
							if err == nil {
								jinfo = string(binfo)
							}
							rerr := rc.SetHashObj(id,
								&eventtype.AlarmDigist{IsAlarm: true, TimeChanged: time.Time(alarmState.TimeChanged).Unix(),
									JInfo:         jinfo,
									TimeRefreshed: now})

							return rerr
						}
						return nil
					}

					if err == nil {
						pAlarmDB.UID = alarmState.UID
						dberr = tx.Save(pAlarmDB).Error
					} else {
						dberr = tx.Create(pAlarmDB).Error
					}
				}
				if dberr == nil {
					//fmt.Println("begin set hash obj--------------",id)
					binfo, err := msgpack.Marshal(curAlarm.Info)
					var jinfo = ""
					if err == nil {
						jinfo = string(binfo)
					}

					rerr := rc.SetHashObj(id, &eventtype.AlarmDigist{IsAlarm: true, TimeChanged: curAlarm.Time, TimeRefreshed: now,
						JInfo: jinfo})
					if rerr != nil {
						fmt.Println("redis error------------------", rerr)
					}
					curAlarm.AlarmDisp = pAlarmDB.AlarmDisp
					if isOfflineAlarm {
						//rc.HSet("thing."+curAlarm.TID+".", "State", "offline")
						//if(pThing.Thing.)
						//来自非自身管理的设备的告警，需要更新内部状态
						if curAlarm.Checker != "onlineChecker" {
							cache.SetThingState(curAlarm.TID, "offline", tnow)
						}
					}

					notify.PubMsg(false, "alarm", curAlarm)
					if alarmChan != nil {
						alarmChan <- curAlarm
					}

				} else {
					fmt.Println("dberror------", dberr)
				}
				return dberr
			})
			if errdb == nil {
				//rc.HSetField("currAlarms."+curAlarm.TID+".", makeAlarmIDForThing(curAlarm), curAlarm.Time)
				cache.SaveAlarmToTid(curAlarm)
			} else {
				logger.SLog.Error("db error--------", curAlarm, errdb)
			}

		} else {
			rc.HSetField(id, "TimeRefreshed", now)
		}
		//alarmActive, errDB := saveToDB(curAlarm,id)*/
}

//ThingAlarmClear 告警消除,告警延时主要在消除时处理，当告警消除来临时，看上次变化时间，如果短于告警延时，
//告警消除放入redis中，当告警来临时会把放入redis中的告警消除删除
//在定时器中会把目前已经到时间的消除放出来进行处理
func ThingAlarmClear(curAlarm *eventtype.Alarm) {
	//handleAlarmClear(curAlarm, false)
	handler.HandleClear(curAlarm)

}

//ClearOfflineAlarm 消除断线告警
func ClearOfflineAlarm(tid string) {
	pAlarmClear := &eventtype.Alarm{
		Flag: "END",
	}
	pAlarmClear.TID = tid
	pAlarmClear.AlarmID = "offline"
	ThingAlarmClear(pAlarmClear)
}

/*
func handleAlarmClear(curAlarm *eventtype.Alarm, noDelay bool) {

		pThing, err := cache.GetThing(curAlarm.TID)
		if err != nil || pThing == nil {
			return
		}
		tnow := time.Now()
		now := tnow.Unix()
		if curAlarm.ClearTime == 0 {
			curAlarm.ClearTime = now
		}
		id := cache.GetActiveAlarmID(curAlarm)
		pAlarmInRedis, _, _ := getAlarmDigit(id, now, &curAlarm.AlarmKey)

		if !pAlarmInRedis.IsAlarm || pAlarmInRedis.TimeChanged == 0 { //当没有数据时，消除也没有意义；当前是消除状态，消除也无意义
			cache.RemoveAlarm(curAlarm)
			return
		}
		if pAlarmInRedis.TimeChanged < curAlarm.ClearTime { //此时为告警状态，且消除时间大于告警时间，此消除有效
			if !noDelay && pAlarmInRedis.Delay > 0 {
				var ckey = "cache_clear_" + id
				var reachTime = float64(curAlarm.ClearTime + pAlarmInRedis.Delay)
				msgValues, _ := msgpack.Marshal(curAlarm)
				rc.HSetField(ckey, "clearTime", curAlarm.ClearTime, "obj", msgValues)
				rc.AddScore("cache_clear", ckey, reachTime)
				cache.RemoveAlarm(curAlarm)
				return
			}

			curAlarm.ThingName = pThing.Thing.SName
			curAlarm.StationID = pThing.StationID
			station, _ := cache.GetThing(pThing.StationID)
			if station != nil {
				curAlarm.StationName = station.Thing.SName
			}
			model, _ := cache.GetModelOption(pThing.Thing.ModelID)
			if model != nil {
				curAlarm.CatalogID = model.CatalogID
			}
			var alarmHistroy eventtype.AlarmHistroy
			dberr := dbconn.DBConnection.Transaction(func(tx *gorm.DB) error {
				var alarmState eventtype.AlarmState
				err := tx.Model(&alarmState).Where("t_id=? and mindex=?  and alarm_id=?",
					curAlarm.TID, curAlarm.Mindex, curAlarm.AlarmID).First(&alarmState).Error
				//找到当前数据库中的状态
				if err == nil { //找到数据库中的告警
					if alarmState.IsAlarm {
						if time.Time(alarmState.TimeChanged).Unix() < curAlarm.ClearTime {
							clearTime := time.Unix(curAlarm.ClearTime, 0)
							clearObj, _ := json.Marshal(curAlarm.Info)
							curAlarm.AlarmDisp = alarmState.AlarmDisp
							//alarmHistroy := eventtype.AlarmHistroy{
							alarmHistroy.Seq = alarmState.UID
							alarmHistroy.AlarmKey = alarmState.AlarmKey
							alarmHistroy.AlarmTime = eventtype.Datetime(alarmState.TimeChanged)
							alarmHistroy.ClearTime = eventtype.Datetime(clearTime)
							alarmHistroy.AlarmObj = alarmState.AlarmObj
							alarmHistroy.ClearObj = clearObj
							alarmHistroy.AlarmDisp = alarmState.AlarmDisp
							alarmHistroy.StationName = curAlarm.StationName
							alarmHistroy.StationID = curAlarm.StationID
							alarmHistroy.CatalogID = curAlarm.CatalogID
							//}
							alarmState.IsAlarm = false
							alarmState.TimeChanged = eventtype.Datetime(clearTime)
							//jobj, _ := clearObj
							alarmState.AlarmObj = clearObj
							err = tx.Save(&alarmState).Error
							/*if err == nil {
								err = tx.Create(&alarmHistroy).Error
							}*/
/*
							if err != nil {
								fmt.Println("dberror------", err)
								logger.SLog.Error("--------alarm error------------", err)
								err = nil
							}
							curAlarm.AlarmDisp = alarmState.AlarmDisp

							rerr := rc.SetHashObj(id, &eventtype.AlarmDigist{IsAlarm: false, TimeChanged: curAlarm.ClearTime, TimeRefreshed: now})
							if rerr != nil {
								fmt.Println("redis error------", rerr)
							}
							return rerr

						}
					} else {
						return errors.New("is_alarm is false")
					}
				}
				return err
			})
			if dberr == nil {
				//cache.HDelField("currAlarms."+curAlarm.TID+".", makeAlarmIDForThing(curAlarm))
				cache.RemoveAlarm(curAlarm)
				if curAlarm.AlarmID == "offline" {
					//rc.HSet("thing."+curAlarm.TID+".", "State", "online")
					//dbconn.DBConnection.Save(&dbobj.ThingState{ThingID: curAlarm.TID,
					//StationID:   curAlarm.StationID,
					//StationName: curAlarm.StationName,
					//State: 1, ChangedTime: clearTime})
					if curAlarm.Checker != "onlineChecker" {
						cache.SetThingState(curAlarm.TID, "online", tnow)
						fmt.Println("--------set state online--@@@@@@@@@@---------", curAlarm.TID)
					}

				}
				dbconn.DBConnection.Create(&alarmHistroy)
				notify.PubMsg(false, "alarm", curAlarm)
				if alarmChan != nil {
					alarmChan <- curAlarm
				}
			} else {
				logger.SLog.Error("--------alarm error------------", dberr)
				fmt.Println("--------alarm error------------", dberr)
			}
		}
		rc.HSetField(id, "TimeRefreshed", now)
}*/

/*
func delayClearAlarm() {
	timer := time.NewTimer(5 * time.Second)
	for {
		timer.Reset(5 * time.Second) // 这里复用了 timer
		select {
		case <-timer.C:
			now := float64(time.Now().Unix())
			ids, err := rc.ZRangeByScore("cache_clear", 0, now)
			if err == nil && len(ids) > 0 {
				var alarmClear eventtype.Alarm
				for _, id := range ids {
					sobj, err := rc.GetHashField(id, "obj").Result()
					if err == nil {
						err := msgpack.Unmarshal([]byte(sobj), &alarmClear)
						if err == nil {
							handleAlarmClear(&alarmClear, true)
						}
					}
					rc.RemoveKeyFromSet("cache_clear", id)
					rc.RemoveKey(id)

				}
			}
		}
	}

}
*/
